//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
//-----------------------------------------------------------------------------
// <auto-generated>
//   This code was generated by a tool.
//
//   Changes to this file may cause incorrect behavior and will be lost if
//   the code is regenerated.
//
//   For more information, see: http://go.microsoft.com/fwlink/?LinkID=623246
// </auto-generated>
//-----------------------------------------------------------------------------
#include "pch.h"

using namespace concurrency;
using namespace Microsoft::WRL;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Devices::AllJoyn;
using namespace com::microsoft::Samples::SecureInterface;

std::map<alljoyn_interfacedescription, WeakReference*> SecureInterfaceConsumer::SourceInterfaces;
PCSTR SecureInterfaceConsumer::m_interfaceName = "com.microsoft.Samples.SecureInterface";

SecureInterfaceConsumer::SecureInterfaceConsumer(AllJoynBusAttachment^ busAttachment)
    : m_busAttachment(busAttachment),
    m_proxyBusObject(nullptr),
    m_busObject(nullptr)
{
    m_weak = new WeakReference(this);
    m_signals = ref new SecureInterfaceSignals();
    m_nativeBusAttachment = AllJoynHelpers::GetInternalBusAttachment(m_busAttachment);

    GUID result;
    HRESULT hr = CoCreateGuid(&result);

    if (FAILED(hr))
    {
        throw ref new Exception(hr);
    }

    // The consumer needs a bus object to share signals, and its object path must be unique in
    // case multiple consumers are created using the same bus attachment.
    Guid gd(result);
    ServiceObjectPath = gd.ToString();
}

SecureInterfaceConsumer::~SecureInterfaceConsumer()
{
    if (nullptr != ProxyBusObject)
    {
        alljoyn_proxybusobject_destroy(ProxyBusObject);
    }
    delete m_weak;
}

QStatus SecureInterfaceConsumer::AddSignalHandler(_In_ alljoyn_busattachment busAttachment, _In_ alljoyn_interfacedescription interfaceDescription, _In_ PCSTR methodName, _In_ alljoyn_messagereceiver_signalhandler_ptr handler)
{
    alljoyn_interfacedescription_member member;
    if (!alljoyn_interfacedescription_getmember(interfaceDescription, methodName, &member))
    {
        return ER_BUS_INTERFACE_NO_SUCH_MEMBER;
    }

    return alljoyn_busattachment_registersignalhandler(busAttachment, handler, member, NULL);
}

IAsyncOperation<SecureInterfaceConsumer^>^ SecureInterfaceConsumer::FromIdAsync(_In_ Platform::String^ deviceId)
{
    return SecureInterfaceConsumer::FromIdAsync(deviceId, AllJoynBusAttachment::GetDefault());
}

IAsyncOperation<SecureInterfaceConsumer^>^ SecureInterfaceConsumer::FromIdAsync(_In_ Platform::String^ deviceId, _In_ AllJoynBusAttachment^ busAttachment)
{
    return create_async([deviceId, busAttachment]() -> SecureInterfaceConsumer^
    {
        SecureInterfaceConsumer^ result;
        create_task(AllJoynServiceInfo::FromIdAsync(deviceId)).then([busAttachment, &result](AllJoynServiceInfo^ serviceInfo)
        {
            if (serviceInfo != nullptr)
            {
                int32 status = AllJoynStatus::Ok;
                if (busAttachment->State == AllJoynBusAttachmentState::Disconnected)
                {
                    event connectedEvent;
                    auto token = busAttachment->StateChanged += ref new TypedEventHandler<AllJoynBusAttachment^, AllJoynBusAttachmentStateChangedEventArgs^>([&connectedEvent](AllJoynBusAttachment^, AllJoynBusAttachmentStateChangedEventArgs^ arg)
                    {
                        if (arg->State == AllJoynBusAttachmentState::Connected)
                        {
                            connectedEvent.set();
                        }
                    });

                    status = AllJoynHelpers::CreateInterfaces(busAttachment, c_SecureInterfaceIntrospectionXml);
                    if (status == AllJoynStatus::Ok)
                    {
                        busAttachment->Connect();
                        connectedEvent.wait();
                    }
                    busAttachment->StateChanged -= token;
                }

                if (status == AllJoynStatus::Ok)
                {
                    auto consumer = ref new SecureInterfaceConsumer(busAttachment);
                    status = consumer->Initialize(serviceInfo);
                    if (status == AllJoynStatus::Ok)
                    {
                        result = consumer;
                    }
                }
            }
        }).wait();

        return result;
    });
}

IAsyncOperation<SecureInterfaceConcatenateResult^>^ SecureInterfaceConsumer::ConcatenateAsync(_In_ Platform::String^ interfaceMemberInStr1, _In_ Platform::String^ interfaceMemberInStr2)
{
    return create_async([this, interfaceMemberInStr1, interfaceMemberInStr2]() -> SecureInterfaceConcatenateResult^
    {
        auto result = ref new SecureInterfaceConcatenateResult();

        alljoyn_message message = alljoyn_message_create(m_nativeBusAttachment);
        size_t argCount = 2;
        alljoyn_msgarg inputs = alljoyn_msgarg_array_create(argCount);

        QStatus status = ER_OK;
        status = static_cast<QStatus>(TypeConversionHelpers::SetAllJoynMessageArg(alljoyn_msgarg_array_element(inputs, 0), "s", interfaceMemberInStr1));
        if (ER_OK == status)
        {
            status = static_cast<QStatus>(TypeConversionHelpers::SetAllJoynMessageArg(alljoyn_msgarg_array_element(inputs, 1), "s", interfaceMemberInStr2));
        }
        if (ER_OK == status)
        {
            status = alljoyn_proxybusobject_methodcall(
                ProxyBusObject,
                m_interfaceName,
                "Concatenate",
                inputs,
                argCount,
                message,
                c_MessageTimeoutInMilliseconds,
                0);
        }
        result->Status = static_cast<int>(status);
        if (ER_OK == status)
        {
            result->Status = AllJoynStatus::Ok;
            Platform::String^ argument0;
            status = static_cast<QStatus>(TypeConversionHelpers::GetAllJoynMessageArg(alljoyn_message_getarg(message, 0), "s", &argument0));
            result->OutStr = argument0;

            if (ER_OK != status)
            {
                result->Status = static_cast<int>(status);
            }
        }
        else if (ER_BUS_REPLY_IS_ERROR_MESSAGE == status)
        {
            alljoyn_msgarg errorArg = alljoyn_message_getarg(message, 1);
            if (nullptr != errorArg)
            {
                uint16 errorStatus;
                status = alljoyn_msgarg_get_uint16(errorArg, &errorStatus);
                if (ER_OK == status)
                {
                    status = static_cast<QStatus>(errorStatus);
                }
            }
            result->Status = static_cast<int>(status);
        }

        alljoyn_message_destroy(message);
        alljoyn_msgarg_destroy(inputs);

        return result;
    });
}

IAsyncOperation<SecureInterfaceSetIsUpperCaseEnabledResult^>^ SecureInterfaceConsumer::SetIsUpperCaseEnabledAsync(_In_ bool value)
{
    return create_async([this, value]() -> SecureInterfaceSetIsUpperCaseEnabledResult^
    {
        PropertySetContext setContext;

        alljoyn_msgarg inputArgument = alljoyn_msgarg_create();
        QStatus status = static_cast<QStatus>(TypeConversionHelpers::SetAllJoynMessageArg(inputArgument, "b", value));
        if (ER_OK == status)
        {
            status = alljoyn_proxybusobject_setpropertyasync(
                ProxyBusObject,
                m_interfaceName,
                "IsUpperCaseEnabled",
                inputArgument,
                [](QStatus status, alljoyn_proxybusobject obj, void* context)
                {
                    UNREFERENCED_PARAMETER(obj);
                    auto propertyContext = static_cast<PropertySetContext*>(context);
                    propertyContext->SetStatus(status);
                    propertyContext->SetEvent();
                },
                c_MessageTimeoutInMilliseconds,
                &setContext);

            alljoyn_msgarg_destroy(inputArgument);

            setContext.Wait();
        }
        if (ER_OK == status)
        {
            return SecureInterfaceSetIsUpperCaseEnabledResult::CreateSuccessResult();
        }
        return SecureInterfaceSetIsUpperCaseEnabledResult::CreateFailureResult(static_cast<int>(status));
    });
}

IAsyncOperation<SecureInterfaceGetIsUpperCaseEnabledResult^>^ SecureInterfaceConsumer::GetIsUpperCaseEnabledAsync()
{
    return create_async([this]() -> SecureInterfaceGetIsUpperCaseEnabledResult^
    {
        PropertyGetContext<bool> getContext;

        alljoyn_proxybusobject_getpropertyasync(
            ProxyBusObject,
            m_interfaceName,
            "IsUpperCaseEnabled",
            [](QStatus status, alljoyn_proxybusobject obj, const alljoyn_msgarg value, void* context)
            {
                UNREFERENCED_PARAMETER(obj);
                auto propertyContext = static_cast<PropertyGetContext<bool>*>(context);

                if (ER_OK == status)
                {
                    bool argument;
                    status = static_cast<QStatus>(TypeConversionHelpers::GetAllJoynMessageArg(value, "b", &argument));

                    propertyContext->SetValue(argument);
                }
                propertyContext->SetStatus(status);
                propertyContext->SetEvent();
            },
            c_MessageTimeoutInMilliseconds,
            &getContext);

        getContext.Wait();

        auto result = ref new SecureInterfaceGetIsUpperCaseEnabledResult();
        result->Status = getContext.GetStatus();
        result->IsUpperCaseEnabled = getContext.GetValue();
        return result;
    });
}

void SecureInterfaceConsumer::OnPropertyChanged(_In_ alljoyn_proxybusobject obj, _In_ PCSTR interfaceName, _In_ const alljoyn_msgarg changed, _In_ const alljoyn_msgarg invalidated)
{
    UNREFERENCED_PARAMETER(obj);
    UNREFERENCED_PARAMETER(interfaceName);
    UNREFERENCED_PARAMETER(invalidated);

    alljoyn_msgarg changedProperties;
    size_t changedPropertyCount;
    if (ER_OK != alljoyn_msgarg_get(changed, "a{sv}", &changedPropertyCount, &changedProperties))
    {
        return;
    }

    for (size_t i = 0; i < changedPropertyCount; i++)
    {
        char* propertyName;
        alljoyn_msgarg propertyValue;
        if (ER_OK != alljoyn_msgarg_get(alljoyn_msgarg_array_element(changedProperties, i), "{sv}", &propertyName, &propertyValue))
        {
            return;
        }


        if (strcmp("IsUpperCaseEnabled", propertyName) == 0)
        {
            bool argument;
            (void)TypeConversionHelpers::GetAllJoynMessageArg(propertyValue, "b", &argument);
            IsUpperCaseEnabledChanged(this, (Platform::Object^)argument);
        }
    }
}

void SecureInterfaceConsumer::CallTextSentSignalHandler(_In_ const alljoyn_interfacedescription_member* member, _In_ alljoyn_message message)
{
    auto source = SourceInterfaces.find(member->iface);
    if (source == SourceInterfaces.end())
    {
        return;
    }

    auto consumer = source->second->Resolve<SecureInterfaceConsumer>();
    if (consumer->Signals != nullptr)
    {
        auto callInfo = ref new AllJoynMessageInfo(AllJoynHelpers::MultibyteToPlatformString(alljoyn_message_getsender(message)));
        auto eventArgs = ref new SecureInterfaceTextSentReceivedEventArgs();
        eventArgs->MessageInfo = callInfo;

        Platform::String^ argument0;
        (void)TypeConversionHelpers::GetAllJoynMessageArg(alljoyn_message_getarg(message, 0), "s", &argument0);

        eventArgs->Message = argument0;

        consumer->Signals->CallTextSentReceived(consumer->Signals, eventArgs);
    }
}

int32 SecureInterfaceConsumer::Initialize(_In_ AllJoynServiceInfo^ serviceInfo)
{
    std::vector<char> sessionNameUtf8 = AllJoynHelpers::PlatformToMultibyteString(serviceInfo->UniqueName);

    ServiceObjectPath = serviceInfo->ObjectPath;
    std::vector<char> objectPath = AllJoynHelpers::PlatformToMultibyteString(ServiceObjectPath);

    RETURN_IF_QSTATUS_ERROR(AllJoynHelpers::CreateInterfaces(m_busAttachment, c_SecureInterfaceIntrospectionXml));

    m_session = create_task(AllJoynSession::GetFromServiceInfoAsync(serviceInfo, m_busAttachment)).get();
    if (nullptr == m_session)
    {
        return AllJoynStatus::Fail;
    }
    else if (m_session->Status != AllJoynStatus::Ok)
    {
        return m_session->Status;
    }

    if (objectPath.empty())
    {
        return AllJoynStatus::Fail;
    }

    ProxyBusObject = alljoyn_proxybusobject_create(m_nativeBusAttachment, &sessionNameUtf8[0], &objectPath[0], m_session->Id);
    if (nullptr == ProxyBusObject)
    {
        return AllJoynStatus::Fail;
    }

    PCSTR propertyNames[] = { "IsUpperCaseEnabled" };

    RETURN_IF_QSTATUS_ERROR(alljoyn_proxybusobject_registerpropertieschangedlistener(
        ProxyBusObject,
        m_interfaceName,
        propertyNames,
        _countof(propertyNames),
        AllJoynHelpers::PropertyChangedHandler<SecureInterfaceConsumer>,
        m_weak));


    alljoyn_interfacedescription description = alljoyn_busattachment_getinterface(m_nativeBusAttachment, m_interfaceName);
    if (nullptr == description)
    {
        return AllJoynStatus::Fail;
    }

    m_busObject = ref new Windows::Devices::AllJoyn::AllJoynBusObject(ServiceObjectPath, m_busAttachment);
    m_nativeBusObject = AllJoynHelpers::GetInternalBusObject(m_busObject);

    QStatus status = alljoyn_busobject_addinterface(m_nativeBusObject, description);
    if ((status != ER_OK) && (status != ER_BUS_IFACE_ALREADY_EXISTS))
    {
        return status;
    }


    QStatus result = AddSignalHandler(
        m_nativeBusAttachment,
        description,
        "TextSent",
        [](const alljoyn_interfacedescription_member* member, PCSTR srcPath, alljoyn_message message) { UNREFERENCED_PARAMETER(srcPath); CallTextSentSignalHandler(member, message); });

    if (ER_OK != result)
    {
        return static_cast<int32>(result);
    }

    SourceInterfaces[description] = m_weak;

    unsigned int noneMechanismIndex = 0;
    bool authenticationMechanismsContainsNone = m_busAttachment->AuthenticationMechanisms->IndexOf(AllJoynAuthenticationMechanism::None, &noneMechanismIndex);
    QCC_BOOL interfaceIsSecure = alljoyn_interfacedescription_issecure(description);

    // If the current set of AuthenticationMechanisms supports authentication,
    // determine whether to secure the connection.
    if (AllJoynHelpers::CanSecure(m_busAttachment->AuthenticationMechanisms))
    {
        // Secure the connection if the org.alljoyn.Bus.Secure XML annotation
        // is specified, or if None is not present in AuthenticationMechanisms.
        if (!authenticationMechanismsContainsNone || interfaceIsSecure)
        {
            RETURN_IF_QSTATUS_ERROR(alljoyn_proxybusobject_secureconnection(ProxyBusObject, QCC_FALSE));
            m_busObject->Start();
        }
        else
        {
            m_busObject->Start();
        }
    }
    else
    {
        // If the current set of AuthenticationMechanisms does not support authentication
        // but the interface requires security, report an error.
        if (interfaceIsSecure)
        {
            return static_cast<int32>(ER_BUS_NO_AUTHENTICATION_MECHANISM);
        }
        else
        {
            m_busObject->Start();
        }
    }

    RETURN_IF_QSTATUS_ERROR(alljoyn_proxybusobject_addinterface(ProxyBusObject, description));

    m_signals->Initialize(this);

    return AllJoynStatus::Ok;
}
